using Fusion.Async.Timers;
using Fusion.Utilities.Bit;
using System;
using System.Net;
using System.Net.Sockets;

namespace Fusion.Net.TCP
{
    public class TCPClientSocket : TCPSocket
    {
        private int packedOffset = 5;
        private byte[] dataPackedArray;

        public TCPClientSocket(TCPNet tcp, AddressFamily addressFamily) : base(tcp, addressFamily)
        {
            dataPackedArray = new byte[packedOffset];
        }

        public TCPClientSocket(TCPNet tcp, Socket socket) : base(tcp, socket)
        {
            dataPackedArray = new byte[packedOffset];
        }

        protected override void Invoke()
        {
            Socket.Receive(dataPackedArray, 0, packedOffset, SocketFlags.None);

            byte execution = dataPackedArray[0];
            if (execution == 0)
            {
                int dataLength = BitUtility.ReadInt(dataPackedArray, 1);

                byte[] data = new byte[dataLength];
                int recvedLength = 0;

                while (Socket.Connected && recvedLength < dataLength)
                {
                    recvedLength += Socket.Receive(data, recvedLength, dataLength - recvedLength, SocketFlags.None);
                }
                if (recvedLength >= dataLength)
                {
                    netBase.DataHandler.Enqueue(this, new NetDataHandler.DataWrap(data));
                }
            }
            else if (execution == 1)
            {
                netBase.DataHandler.Enqueue(this, new NetDataHandler.DataWrap(null, NetDataHandler.DataType.HeartBeats));
                heartBeatsChecker?.Reset();
            }
            else if (execution == 2)
            {
                heartBeatsResponseChecker.Pause();
                heartBeatsResponseChecker.Reset();
            }
        }

        protected override void PreOpen()
        {
            socket.Connect(ip);
        }

        protected Timer heartBeatsSender;
        public void SetHeartBeats(float interval)
        {
            heartBeatsSender?.Stop();
            if (interval > 0)
            {
                heartBeatsSender = new Timer().FrequencySetter(interval).PersistentSetter(true).TagSetter("TcpClient_HeartBeatsSender").Invoke(SendHeartBeats);
            }
            else
            {
                heartBeatsSender = null;
            }
        }

        HeartBeatsChecker heartBeatsChecker;
        public void SetHeartBeatsChecker(float interval)
        {
            if (interval > 0)
            {
                if (heartBeatsChecker == null)
                {
                    heartBeatsChecker = new HeartBeatsChecker(interval);
                    heartBeatsChecker.Register(CheckHeartBeats);
                }
                heartBeatsChecker.Interval = interval;
            }
            else
            {
                heartBeatsChecker?.Close();
            }
        }

        HeartBeatsChecker heartBeatsResponseChecker;
        public void SetHeartBeatsResponseChecker(float interval)
        {
            if (interval > 0)
            {
                if (heartBeatsResponseChecker == null)
                {
                    heartBeatsResponseChecker = new HeartBeatsChecker(interval);
                    heartBeatsResponseChecker.Register(CheckHeartBeats);
                }
                heartBeatsResponseChecker.Interval = interval;
                heartBeatsResponseChecker.Pause();
            }
            else
            {
                heartBeatsResponseChecker?.Close();
            }
        }


        private void CheckHeartBeats(EndPoint ip)
        {
            Close(1);
        }

        byte[] heartBeatsData = new byte[] { 1, 0, 0, 0, 0 };
        byte[] heartBeatsResponseData = new byte[] { 2, 0, 0, 0, 0 };

        public void SendHeartBeats()
        {
            SendPackedData(heartBeatsData);
            heartBeatsResponseChecker?.Play();
        }

        public void SendHeartBeatsResponse()
        {
            SendPackedData(heartBeatsResponseData);
        }

        public override void Close(int code = 0)
        {
            if (state == SocketState.Finish)
                return;

            tcpServer?.RemoveClient(this);
            heartBeatsSender?.Stop();
            heartBeatsChecker?.Close();
            heartBeatsResponseChecker?.Close();
            if (socket.Connected)
            {
                socket.Shutdown(SocketShutdown.Both);
            }
            base.Close(code);
        }
    }

}

